package birdsong.dummy;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.LinkedBlockingQueue;

import FollowME.ClientToServerMessage;
import FollowME.ServerToClientMessage;
import birdsong.comm.ClientComm;
import birdsong.comm.ClientSideMessage;
import birdsong.comm.ServerSideMessage.Type;
/**
 * 
 * Communication interface for a Birdsong client. A Birdsong client must
 * communicate with server exclusively through this class.
 * 
 * @author Fernando Pereira, Marcos Martins
 * @version 2.0
 */
public class ClientCommDummy implements ClientComm {

	private Socket socket;
	private ObjectOutputStream oos;
	private String nickname;
	private LinkedBlockingQueue<ServerToClientMessage> receivedMessages;


	/**
	 * Establish a connection to a Birdsong server. This method should be the
	 * first one called by the client as no other operation (expect for
	 * {@link #isConnected()}) can be performed before a connection to the
	 * server has been established.
	 * 
	 * @param host
	 *            Address of the server. For instance "localhost",
	 *            "192.168.1.1", and so on.
	 * @param nickname
	 *            The nickname to use for the client.
	 * @throws UnknownHostException
	 *             Thrown if the host cannot be found.
	 * @throws IOException
	 *             Thrown if a connection cannot be established.
	 */
	@Override
	public void connect(String host, String nickname)
			throws UnknownHostException, IOException {
		  this.nickname = nickname;

		socket = new Socket(host, ServerCommDummy.SERVER_PORT);

		oos = new ObjectOutputStream(socket.getOutputStream());
		receivedMessages = new LinkedBlockingQueue<ServerToClientMessage>();

		// sends a connection message to server
		oos.writeObject(new ClientToServerMessage(Type.CLIENT_CONNECTED, null,
				nickname));

		// for handle the inputs
		new ServerHandler(socket, receivedMessages).start();
	}

	/**
	 * Check if currently connected to a host.
	 * 
	 * @return <b>true</b> if currently connected to a host, <b>false</b> otherwise.
	 */
	@Override
	public boolean isConnected() {
		return !socket.isClosed();
	}

  /**
	 * Disconnect from a Birdsong server. Any sockets should be closed immediately. 
	 * Pending messages are not guaranteed to be delivered before disconnecting.
	 */	
	@Override
	public void disconnect() {
		try {

			oos.writeObject(new ClientToServerMessage(Type.CLIENT_DISCONNECTED,
					null, nickname));
			// close connection.
			socket.close();
		} catch (IOException e) {
			System.out.println("ERROR - On disconnection.");
		}
	}

	/** 
	 * Check if a message from the server is pending. If {@link #hasNextMessage()} returns <b>true</b>, a call to {@link #getNextMessage()} 
	 * will return immediately with the oldest message in the queue. If {@link #hasNextMessage()} returns <b>false</b>, a call to {@link #getNextMessage()} 
	 * will block until a message has been received.
	 * 
	 * @return <b>true</b> if a message from the server is currently pending, otherwise <b>false</b>.
	 */
	@Override
	public boolean hasNextMessage() {
		return receivedMessages.peek() != null;
	}

	/**
	 * Get the next message received from the server. If no message 
	 * is has been received, the method blocks until a message has 
	 * been received. If no message will ever be received (connection is down), null is returned.
	 * 
	 * @return The next message sent by the server to the client or null if no message will ever be received (disconnected from server).
	 */
	@Override
	public ClientSideMessage getNextMessage() {
		ServerToClientMessage message = null;

		if (isConnected()) {
			try {
				message = receivedMessages.take();
			} catch (InterruptedException e) {
				System.out.println("ERROR - Can't get the next message.");
			}
		}
		return message;
	}

	/**
	 * Send a Birdsong message from this client to the server which should then
	 * relay the message to all followers.
	 * 
	 * @param birdsongMessage
	 *            The Birdsong message to be sent.
	 */
	@Override
	public void sendBirdsongMessage(String birdsongMessage) {
		try {
			oos.writeObject(new ClientToServerMessage(Type.BIRDSONG_MESSAGE,
					birdsongMessage, nickname));
		} catch (IOException e) {
			System.out.println("ERROR - Can't send message");
		}

	}

	/**
	 * Request to follow another client.
	 * 
	 * @param nickname
	 *            the nickname of the client that should be followed.
	 */
	@Override
  public void follow(String nickname) {
		try {
			oos.writeObject(new ClientToServerMessage(Type.FOLLOW, nickname,
					this.nickname));
		} catch (IOException e) {
			System.err.println("ERROR - Unable to follow " + nickname);
		}
	}

	/**
	 * Request to unfollow a client.
	 * 
	 * @param nickname
	 *            the nickname of the client currently followed that should be
	 *            unfollowed.
	 */
	@Override
	public void unfollow(String nickname) {
		try {
			oos.writeObject(new ClientToServerMessage(Type.UNFOLLOW, nickname,
					this.nickname));
		} catch (IOException e) {
			System.err.println("ERROR - Unable to unfollow " + nickname);
		}
	}

}